package com.lkdont.cameratest

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.ImageFormat
import android.graphics.Point
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.params.StreamConfigurationMap
import android.media.ImageReader
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Size
import android.view.*
import androidx.lifecycle.lifecycleScope
import com.lkdont.android.base.ui.BaseFrag
import com.lkdont.android.media.examples.R
import kotlinx.android.synthetic.main.frag_take_photo.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
import kotlin.math.max
import kotlin.math.min

/**
 * 拍照页面
 *
 * @author lqd
 */
class TakePhotoFrag : BaseFrag() {

    companion object {

        private val TAG = TakePhotoFrag::class.java.simpleName

    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? = inflater.inflate(R.layout.frag_take_photo, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        previewSv.holder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceChanged(
                holder: SurfaceHolder?,
                format: Int,
                width: Int,
                height: Int
            ) = Unit

            override fun surfaceDestroyed(holder: SurfaceHolder?) = Unit

            override fun surfaceCreated(holder: SurfaceHolder?) {
                ensureCamera()
                val previewSize = getPreviewOutputSize(
                    previewSv.display,
                    cameraCharacteristics,
                    SurfaceHolder::class.java
                )
                previewSv.setAspectRatio(previewSize.width, previewSize.height)

                // 确保尺寸已经被设置
                previewSv.post { initializeCamera() }
            }
        })

    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop: ")
        try {
            camera.close()
        } catch (e: Exception) {
            Log.e(TAG, "onStop: 关闭摄像头失败")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: ")
        cameraThread.quitSafely()
    }

    //******************************** 摄像头相关 ********************************//

    private lateinit var cameraManager: CameraManager
    private lateinit var cameraCharacteristics: CameraCharacteristics

    private lateinit var camera: CameraDevice
    private lateinit var cameraId: String

    private lateinit var imageReader: ImageReader
    private lateinit var cameraSession: CameraCaptureSession

    /** 确认有没有合适的摄像头设备 */
    private fun ensureCamera() {
        cameraManager = requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
        val cameraIds = cameraManager.cameraIdList
        for (id in cameraIds) {
            val cc = cameraManager.getCameraCharacteristics(id)
            if (cc[CameraCharacteristics.LENS_FACING] == CameraCharacteristics.LENS_FACING_BACK) {
                // 后置摄像头
                when (cc[CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL]) {

                    CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
                    CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 -> {
                        // 后置摄像头支持所有的Camera2特性
                        cameraId = id
                        cameraCharacteristics = cc
                        return
                    }

                    else -> {
                    }
                }
            }
        }
        // 没有找到合适的摄像头
        throw IllegalStateException("没有找到合适的摄像头")
    }

    private class SmartSize(width: Int, height: Int) {
        var size = Size(width, height)
        var long = max(size.width, size.height)
        var short = min(size.width, size.height)
        override fun toString() = "SmartSize(${long}x${short})"
    }

    /** Standard High Definition size for pictures and video */
    private val SIZE_1080P: SmartSize = SmartSize(1920, 1080)

    /** Returns a [SmartSize] object for the given [Display] */
    private fun getDisplaySmartSize(display: Display): SmartSize {
        val outPoint = Point()
        display.getRealSize(outPoint)
        return SmartSize(outPoint.x, outPoint.y)
    }

    /**
     * Returns the largest available PREVIEW size. For more information, see:
     * https://d.android.com/reference/android/hardware/camera2/CameraDevice and
     * https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
     */
    private fun <T> getPreviewOutputSize(
        display: Display,
        characteristics: CameraCharacteristics,
        targetClass: Class<T>,
        format: Int? = null
    ): Size {

        // Find which is smaller: screen or 1080p
        val screenSize = getDisplaySmartSize(display)
        val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
        val maxSize = if (hdScreen) SIZE_1080P else screenSize

        // If image format is provided, use it to determine supported sizes; else use target class
        val config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
        checkNotNull(config)

        if (format == null) {
            check(StreamConfigurationMap.isOutputSupportedFor(targetClass)) { "Assertion failed" }
        } else {
            check(config.isOutputSupportedFor(format)) { "Assertion failed" }
        }
        val allSizes = if (format == null)
            config.getOutputSizes(targetClass) else config.getOutputSizes(format)

        // Get available sizes and sort them by area from largest to smallest
        val validSizes = allSizes
            .sortedWith(compareBy { it.height * it.width })
            .map { SmartSize(it.width, it.height) }.reversed()

        // Then, get the largest output size that is smaller or equal than our max size
        return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
    }

    /** 摄像头事务线程 */
    private val cameraThread by lazy {
        HandlerThread("CameraThread").apply { start() }
    }

    /** 摄像头Handler */
    private val cameraHandler: Handler by lazy {
        Handler(cameraThread.looper)
    }

    /**
     * 打开摄像头
     */
    @SuppressLint("MissingPermission")
    private suspend fun openCamera(
        manager: CameraManager,
        cameraId: String,
        handler: Handler?
    ): CameraDevice = suspendCancellableCoroutine { cont ->
        manager.openCamera(cameraId, object : CameraDevice.StateCallback() {

            override fun onOpened(camera: CameraDevice) = cont.resume(camera)

            override fun onDisconnected(camera: CameraDevice) {
                Log.d(TAG, "openCamera : onDisconnected")
            }

            override fun onError(camera: CameraDevice, error: Int) {
                val msg = when (error) {
                    ERROR_CAMERA_DEVICE -> "Fatal (device)"
                    ERROR_CAMERA_DISABLED -> "Device policy"
                    ERROR_CAMERA_IN_USE -> "Camera in use"
                    ERROR_CAMERA_SERVICE -> "Fatal (service)"
                    ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use"
                    else -> "Unknown"
                }
                val exc = RuntimeException("Camera $cameraId error: ($error) $msg")
                Log.e(TAG, exc.message, exc)
                if (cont.isActive) cont.resumeWithException(exc)
            }
        }, handler)
    }

//    /** 拍照Session线程 */
//    private val sessionThread: HandlerThread by lazy {
//        HandlerThread("SessionThread").apply { start() }
//    }
//
//    /** 拍照Session Handler */
//    private val sessionHandler: Handler by lazy {
//        Handler(sessionThread.looper)
//    }

    /**
     * 创建拍照Session
     */
    private suspend fun createCaptureSession(
        device: CameraDevice,
        targets: List<Surface>,
        handler: Handler?
    ): CameraCaptureSession = suspendCoroutine { cont ->
        device.createCaptureSession(targets, object : CameraCaptureSession.StateCallback() {

            override fun onConfigureFailed(session: CameraCaptureSession) {
                val exc = RuntimeException("Camera ${device.id} session configuration failed")
                Log.e(TAG, exc.message, exc)
                cont.resumeWithException(exc)
            }

            override fun onConfigured(session: CameraCaptureSession) = cont.resume(session)

        }, handler)
    }

    /**
     * 对摄像头进行初始化
     */
    private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {

        // 打开摄像头
        camera = openCamera(cameraManager, cameraId, cameraHandler)

        // 获取摄像头能拍摄的最大尺寸
        val size = cameraCharacteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]!!
            // 所有的摄像头都支持Jpeg格式，所以不需要检查
            .getOutputSizes(ImageFormat.JPEG)!!
            .maxBy { it.width * it.height }!!
        imageReader = ImageReader.newInstance(size.width, size.height, ImageFormat.JPEG, 3)

        // 创建拍照Session
        val targets = arrayListOf(previewSv.holder.surface, imageReader.surface)
        cameraSession = createCaptureSession(camera, targets, cameraHandler)

        // 获取预览画面
        val previewRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
            addTarget(previewSv.holder.surface)
        }
        cameraSession.setRepeatingRequest(previewRequest.build(), null, cameraHandler)
    }
}